import json

from django.http import JsonResponse
from django.shortcuts import render

# Create your views here.
from django.views import View
from rest_framework.response import Response
from rest_framework.views import APIView

from bookmanagment_2.models import BooInfo, HeroInfo
from bookmanagment_2.serializers import BooInfoSerializer, HeroInfoSerializer, HeroInfoModelSerializer

"""
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON/表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中
这样,无论前端发送的是哪种格式数据,都可以以统一的方式读取数据,request.data(请求体的数据,类型:字典)/request.query_params(查询字符串的数据,类型:字典)/request.path/request.user/request.method/request.COOKIES/request.session/
request.data返回解析之后的请求体数据,类似Django中标准的request.POST和request.FILES属性,但提供如下属性:
    1. 包含解析之后的文件和非文件数据
    2. 包含对POST/PUT/PATCH请求方式解析后的数据
    3. 利用REST framework的parsers解析器,不仅支持表单类型数据,也支持json数据
request.query_params与Django标准的request.GET相同

"""


class BooTestView(View):
    def get(self, requset, pk):
        """查询一个"""
        book = BooInfo.objects.get(pk=pk)
        """
        定义BooInfoSerializer序列化器后,此时构造Serializer对象
        语法: Serializer(instance=None, data=empty, **kwarg)
        使用序列化时,将模型类对象传入instance参数
        用于反序列化时,将要被反序列化的数据传入打他参数
        除了instance和打他参数外,在构造Serializer对象时,还可通过context参数额外添加数据,
            如: serializer = AccountSerializer(account, context={'request': request})
                通过context参数附加的数据,可以通过Serializer对象的context属性获取
        """
        # 创建序列化器对象,进行序列化操作
        serializer = BooInfoSerializer(
            book)  # serializer = BooInfoSerializer(instance=book)    BooInfoSerializer(<BooInfo: BooInfo object>):id = ...  btitle = ... ....
        book_dict = serializer.data  # {'image': None, 'bcomment': 36, 'bread': 2, 'bpub_date': '2011-11-11', 'btitle': 'hah', 'id': 1}
        return JsonResponse(book_dict)

    def put(self, request, pk):
        """修改一个对象(即一条记录),全部required=True(默认)的属性"""
        # 获取需要修改的对象
        book = BooInfo.objects.get(pk=pk)
        # 传入修改的属性值
        book_dict = json.loads(request.body.decode())
        # 反序列化验证及修改
        book_serializer = BooInfoSerializer(book, data=book_dict)
        if not book_serializer.is_valid():
            return JsonResponse(book_serializer.errors)
        # 保存
        book = book_serializer.save()
        # 响应
        book_serializer = BooInfoSerializer(book)
        book_dict = book_serializer.data

        return JsonResponse(book_dict, status=201)

    def patch(self, request, pk):
        """修改一个对象(即一条记录),部分required=True(默认)的属性,和put的差别只是添加了一个partial=True属性,其他一样"""
        # 获取需要修改的对象
        book = BooInfo.objects.get(pk=pk)
        # 传入修改的属性值
        book_dict = json.loads(request.body.decode())
        # 反序列化验证及修改
        # partial=True 表示不用修改全部required=True的属性,可以只修改部分属性(即只传部分属性)
        book_serializer = BooInfoSerializer(book, data=book_dict, partial=True)
        if not book_serializer.is_valid():
            return JsonResponse(book_serializer.errors)
        # 保存
        book = book_serializer.save()
        # 响应
        book_serializer = BooInfoSerializer(book)
        book_dict = book_serializer.data

        return JsonResponse(book_dict, status=201)


class HeroTestView(View):
    def get(self, request, pk):
        """查询一个"""
        hero = HeroInfo.objects.get(pk=pk)
        serializer = HeroInfoSerializer(hero)
        hero_dict = serializer.data
        return JsonResponse(hero_dict)


class BoosTestView(View):
    """"""

    def get(self, request):
        """查询所有"""
        # 查询所有图书,序列化
        book_ls = BooInfo.objects.all()
        """
        many参数: 如果关联的对象数据不只有一个,而是包含多个数据时,需在序列化时指明many=True
        """
        book_serializer = BooInfoSerializer(book_ls, many=True)
        book_dict_ls = book_serializer.data
        return JsonResponse(book_dict_ls, safe=False)

    def post(self, request):
        """创建"""
        # 接受数据
        json_dict = json.loads(request.body.decode())

        # 验证,  在构造Serializer对象时,可以通过context参数额外添加数据,如下
        boo_serializer = BooInfoSerializer(data=json_dict, context={'btest_context': json_dict.get("btest_context")})
        # 通过context参数附加的数据, 可以通过Serializer对象的context属性获取
        print("11111-----", boo_serializer.context.get('btest_context'))
        # is_valid()方法还可以在验证失败时(返回值为False)抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接受到此异常,会向前端返回HTTP 404 Bad Request响应
        # 这里自定义抛出异常
        if not boo_serializer.is_valid():
            # 如果验证失败,则返回错误信息
            return JsonResponse(boo_serializer.errors)

        # 保存,执行.save()时,会根据前面定义boo_serializer时是否传入instance来判断执行update()还是create()
        boo = boo_serializer.save()

        # 响应
        boo_serializer = BooInfoSerializer(boo)
        boo_dict = boo_serializer.data

        return JsonResponse(boo_dict, status=201)


"""
rest_framework.views.APIView    APIView继承自View
APIView与View的不同之处:
    传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequest对象
    视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式
    任何APIException异常都会被捕获到,并且处理成核实的响应信息
    在进行dispatch()分发前,会对请求进行身份认证/权限检查/流量控制

"""


class HerosAPIView(APIView):
    def get(self, request):
        # 访问路径  GET http://192.168.160.152:8000/bookmanagment/heros/?id=2
        """查询所有(多个)"""
        # 接收参数
        id = request.query_params.get("id")
        print(id)
        # 根据参数查询多个查询
        hero_ls = HeroInfo.objects.filter(pk__lt=id)
        # 序列化
        hero_serializer = HeroInfoModelSerializer(hero_ls, many=True)
        hero_dict_ls = hero_serializer.data
        # 响应
        # return JsonResponse(hero_dict_ls, safe=False)
        """
        Response(data, status=None, template_name=None, headers=None, content_type=None)
        data: 为响应准备的序列化处理后的数据(多为字典)
        status: 状态码,默认200
        template_name: 模板名称,如果使用HTMLRenderer 时需指明
        headers: 用于存放响应头信息的字典
        content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数
        """
        return Response(hero_dict_ls)

    def post(self, request):
        """创建"""
        # 接收参数
        hero_dict = request.data
        # 验证
        hero_serializer = HeroInfoModelSerializer(data=hero_dict)
        if not hero_serializer.is_valid():
            return Response(hero_serializer.errors)
        # 保存
        hero = hero_serializer.save()
        # 响应
        hero_serializer = HeroInfoModelSerializer(hero)
        hero_dict = hero_serializer.data
        return Response(hero_dict, status=201)


class HeroAPIView(APIView):
    def get(self, request, pk):
        """查询一个"""
        try:
            book = HeroInfo.objects.get(pk=pk)
        except:
            return Response({'msg': '查询数据失败'})
        # 序列化
        hero_serializer = HeroInfoModelSerializer(book)
        hero_dict = hero_serializer.data

        return Response(hero_dict)

    def update(self, request, pk, partial=False):
        """更新, 修改时调用此方法"""
        # 查询
        try:
            hero = HeroInfo.objects.get(pk=pk)
        except:
            return Response({'msg': '查询数据失败'})
        # 接收数据
        hero_dict = request.data
        # 验证
        hero_serializer = HeroInfoModelSerializer(hero, data=hero_dict, partial=partial)
        if not hero_serializer.is_valid():
            return Response(hero_serializer.errors)
        # 保存
        hero = hero_serializer.save()
        # 响应
        hero_serializer = HeroInfoModelSerializer(hero)
        hero_dict = hero_serializer.data
        return Response(hero_dict, status=201)

    def put(self, request, pk):
        """修改所有属性"""
        return self.update(request, pk)

    def patch(self, request, pk):
        """修改部分属性"""
        return self.update(request, pk, True)

    def delete(self, request, pk):
        """删除"""
        try:
            hero = HeroInfo.objects.get(pk=pk)
        except:
            return Response({'msg': '查询失败'})
        hero.delete()
        return Response(status=204)
